﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CkSoftware.GroupMe.Sdk.Bot.Commands;
using CkSoftware.GroupMe.Sdk.Bot.Dao;
using CkSoftware.GroupMe.Sdk.Core;
using CkSoftware.GroupMe.Sdk.Core.Dao;
using CkSoftware.GroupMe.Sdk.Core.Exceptions;

namespace CkSoftware.GroupMe.Sdk.Bot
{
	/// <summary>
	/// Factory class to craete and index any GroupMe bots.
	/// </summary>
	public class GroupMeBotFactory
	{
		private readonly IWebApiAccess _webApiAccess;

		/// <summary>
		/// Instantiates the <see cref="GroupMeBotFactory" /> with the given <see cref="IWebApiAccess" />.
		/// </summary>
		/// <param name="webApiAccess">The <see cref="IWebApiAccess" /> to use.</param>
		public GroupMeBotFactory(IWebApiAccess webApiAccess)
		{
			_webApiAccess = webApiAccess;
		}

		/// <summary>
		/// Instantiates a new <see cref="GroupMeBot" /> class with the given bot-name and group id. The bot is NOT started
		/// already, he must be started manually by using the StartBot() or StartBotAsync() method.
		/// </summary>
		/// <param name="name">The name of the new Bot.</param>
		/// <param name="groupId">The group id where the bot should live in.</param>
		/// <returns>The constructed <see cref="GroupMeBot" /> class.</returns>
		public GroupMeBot CreateBot(string name, string groupId)
		{
			return new GroupMeBot(_webApiAccess, name, groupId);
		}

		/// <summary>
		/// Reads all currently registered bots at GroupMe and returns them as <see cref="GroupMeBot" /> instances, ready to send
		/// messages.
		/// </summary>
		/// <returns>Awaitable <see cref="Task" /> resulting in a list of <see cref="GroupMeBot" /> instances.</returns>
		public async Task<IEnumerable<GroupMeBot>> GetAllBots()
		{
			IEnumerable<BotData> data = await GetAllBotData();
			return data.Select(bot => new GroupMeBot(_webApiAccess, bot));
		}

		/// <summary>
		/// Tries to find the bot using the given bot-name and group id.
		/// </summary>
		/// <param name="name">The bot name to search for (case insensitive).</param>
		/// <param name="groupId">the group-id where the bot should live in.</param>
		/// <exception cref="InvalidOperationException">Occurs when there is no bot with the given name and group-id.</exception>
		/// <returns>
		/// Awaitable task resulting in an instance of <see cref="GroupMeBot" /> if the bot was found. Throws exception if
		/// not!
		/// </returns>
		public async Task<GroupMeBot> GetFirstBotByNameAndGroup(string name, string groupId)
		{
			IEnumerable<BotData> data = await GetAllBotData();
			return data
				.Where(bot =>
					bot.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase) &&
					bot.GroupId.Equals(groupId, StringComparison.InvariantCultureIgnoreCase))
				.Select(bot => new GroupMeBot(_webApiAccess, bot))
				.First();
		}

		private async Task<IEnumerable<BotData>> GetAllBotData()
		{
			var indexCommand = new BotIndexCommand();
			GroupMeApiResponse<BotData[]> botData = await _webApiAccess.ExecuteCommand(indexCommand);

			if (botData.Meta.ParsedCode == GroupMeApiResultCode.Ok)
			{
				return botData.Response;
			}

			throw new GroupMeException(botData.Meta);
		}
	}
}